CloudFront+S3とCodePipelineをCFnでデプロイする – その2
はじめに
この記事は、前回のCloudFront+S3とCodePipelineをCFnでデプロイする - その1の後半記事です。
構成図
CLoudFront、S3、ACM、Route53関連はデプロイが完了しているのでCodepipelineのテンプレートを作成して親スタックに追加していきます。
CodeStarSoruceConnectionテンプレートの作成
ソースプロバイダがGitHubの場合、GitHubバージョン2アクションを利用することが推奨されています。
GitHubバージョン 1 のソースアクションをGitHubバージョン 2 のソースアクションに更新する
CodeStarSourceConnectionリソースが必要です。リソース作成後のGithub側の操作は手動の実施する必要がある為、親スタックと別にスタックを作成します。テンプレートは以下の通りです。
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
SystemName:
Type: String
Default: example
ProviderType:
Type: String
Default: GitHub
AllowedValues:
- Bitbucket
- GitHub
- GitHubEnterpriseServer
Resources:
SourceConnection:
Type: 'AWS::CodeStarConnections::Connection'
Properties:
ConnectionName: !Sub ${SystemName}-Con
ProviderType: !Ref ProviderType
Outputs:
SourceConnection:
Value: !Ref SourceConnection
Export:
Name: !Sub ${SystemName}-ConnectionARN
上記テンプレートでスタックを作成するとスタックはCREATE_COMPLETEになります。Connectionリソースが作成されてステータスが保留中となっているので「保留中の接続を更新」クリックします。
別ウィンドウが表示されるので「新しいアプリをインストールする」をクリックします。
GitHubにサインインします。サインインすると↑の画面に戻るので再度「新しいアプリをインストールする」をクリックします。
AWS Connector for GitHubアプリをインストールするGitHubアカウントをクリックします。
AWS Connector for GitHubアプリがアクセスできるリポジトリを選択できます。今回はAll repositoriesを選択してInstallします。リポジトリを指定する場合はインストール後にGitHubアカウントで追加やAll repositoriesに変更できます。
インストールが終わると以下の画面で接続をクリックするとConnectionリソースのステータスが利用可能となります。
CodePipelineをデプロイする事前準備が完了です。それではCodePipelineテンプレートを作成していきます。アーティファクトストア用のS3バケットテンプレート、CodePipeline用IAMテンプレート、CodePipeline(CodeBuild含む)テンプレートを作成します。
S3バケットテンプレートの作成(子スタック)
アーティファクトストア用のS3バケットのテンプレートです。バケットを作成するだけなので無理して分割しなくても良いですが、バケットポリシーやアクセス管理、ライフサイクルを更新しやすくするために分割しています。
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
SystemName:
Type: String
Resources:
S3BucketArtifactStore:
Type: 'AWS::S3::Bucket'
DeletionPolicy: 'Retain'
Properties:
BucketName: !Sub 'codepipeline-${AWS::Region}-${AWS::AccountId}'
Outputs:
S3BucketArtifactStore:
Value: !Ref S3BucketArtifactStore
Export:
Name: !Sub '${SystemName}-S3BucketArtifactStore'
S3BucketArtifactStoreARN:
Value: !GetAtt S3BucketArtifactStore.Arn
Export:
Name: !Sub '${SystemName}-S3BucketArtifactStoreARN'
CodePipeline用IAMテンプレートの作成(子スタック)
CodePipeline、CodeBuildが利用するサービスロールを作成します。ロールおよびポリシーの内容は、GUIで自動作成される内容とほぼ同等です。
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
SystemName:
Type: String
Resources:
IAMCodePipelineRole:
Type: 'AWS::IAM::Role'
Properties:
Path: '/service-role/'
RoleName: !Sub '${SystemName}-pipeline-ServiceRole'
AssumeRolePolicyDocument: "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"codepipeline.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"
MaxSessionDuration: 3600
ManagedPolicyArns:
- !Ref IAMCodePipelinePolicy
IAMCodePipelinePolicy:
Type: 'AWS::IAM::ManagedPolicy'
Properties:
ManagedPolicyName: !Sub '${SystemName}-pipeline-policy'
Path: '/service-role/'
PolicyDocument: |
{
"Statement": [
{
"Action": [
"iam:PassRole"
],
"Resource": "*",
"Effect": "Allow",
"Condition": {
"StringEqualsIfExists": {
"iam:PassedToService": [
"cloudformation.amazonaws.com",
"elasticbeanstalk.amazonaws.com",
"ec2.amazonaws.com",
"ecs-tasks.amazonaws.com"
]
}
}
},
{
"Action": [
"codecommit:CancelUploadArchive",
"codecommit:GetBranch",
"codecommit:GetCommit",
"codecommit:GetRepository",
"codecommit:GetUploadArchiveStatus",
"codecommit:UploadArchive"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"codedeploy:CreateDeployment",
"codedeploy:GetApplication",
"codedeploy:GetApplicationRevision",
"codedeploy:GetDeployment",
"codedeploy:GetDeploymentConfig",
"codedeploy:RegisterApplicationRevision"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"codestar-connections:UseConnection"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"elasticbeanstalk:*",
"ec2:*",
"elasticloadbalancing:*",
"autoscaling:*",
"cloudwatch:*",
"s3:*",
"sns:*",
"cloudformation:*",
"rds:*",
"sqs:*",
"ecs:*"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"lambda:InvokeFunction",
"lambda:ListFunctions"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"opsworks:CreateDeployment",
"opsworks:DescribeApps",
"opsworks:DescribeCommands",
"opsworks:DescribeDeployments",
"opsworks:DescribeInstances",
"opsworks:DescribeStacks",
"opsworks:UpdateApp",
"opsworks:UpdateStack"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"cloudformation:CreateStack",
"cloudformation:DeleteStack",
"cloudformation:DescribeStacks",
"cloudformation:UpdateStack",
"cloudformation:CreateChangeSet",
"cloudformation:DeleteChangeSet",
"cloudformation:DescribeChangeSet",
"cloudformation:ExecuteChangeSet",
"cloudformation:SetStackPolicy",
"cloudformation:ValidateTemplate"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"codebuild:BatchGetBuilds",
"codebuild:StartBuild",
"codebuild:BatchGetBuildBatches",
"codebuild:StartBuildBatch"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Effect": "Allow",
"Action": [
"devicefarm:ListProjects",
"devicefarm:ListDevicePools",
"devicefarm:GetRun",
"devicefarm:GetUpload",
"devicefarm:CreateUpload",
"devicefarm:ScheduleRun"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"servicecatalog:ListProvisioningArtifacts",
"servicecatalog:CreateProvisioningArtifact",
"servicecatalog:DescribeProvisioningArtifact",
"servicecatalog:DeleteProvisioningArtifact",
"servicecatalog:UpdateProduct"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"cloudformation:ValidateTemplate"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ecr:DescribeImages"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"states:DescribeExecution",
"states:DescribeStateMachine",
"states:StartExecution"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"appconfig:StartDeployment",
"appconfig:StopDeployment",
"appconfig:GetDeployment"
],
"Resource": "*"
}
],
"Version": "2012-10-17"
}
IAMCodeBuildRole:
Type: 'AWS::IAM::Role'
Properties:
Path: '/service-role/'
RoleName: !Sub '${SystemName}-build-ServiceRole'
AssumeRolePolicyDocument: "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"codebuild.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}"
MaxSessionDuration: 3600
ManagedPolicyArns:
- !Ref IAMCodeBuildPolicy
IAMCodeBuildPolicy:
Type: 'AWS::IAM::ManagedPolicy'
Properties:
ManagedPolicyName: !Sub '${SystemName}-build-Policy'
Path: '/service-role/'
PolicyDocument: !Sub |
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Resource": [
"arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*",
"arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/codebuild/*:*"
],
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
]
},
{
"Effect": "Allow",
"Resource": [
"arn:aws:s3:::codepipeline-${AWS::Region}-*"
],
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:GetObjectVersion",
"s3:GetBucketAcl",
"s3:GetBucketLocation"
]
},
{
"Effect": "Allow",
"Action": [
"codebuild:CreateReportGroup",
"codebuild:CreateReport",
"codebuild:UpdateReport",
"codebuild:BatchPutTestCases",
"codebuild:BatchPutCodeCoverages"
],
"Resource": [
"arn:aws:codebuild:${AWS::Region}:${AWS::AccountId}:report-group/${SystemName}-build-*"
]
}
]
}
Outputs:
IAMCodePipelineRoleARN:
Value: !GetAtt IAMCodePipelineRole.Arn
Export:
Name: !Sub ${SystemName}-IAMCodePipelineRoleARN
IAMCodeBuildRoleARN:
Value: !GetAtt IAMCodeBuildRole.Arn
Export:
Name: !Sub ${SystemName}-IAMCodeBuildRoleARN
CodePipelineテンプレートの作成(子スタック)
CodePipelineテンプレートを作成します。Codebuildビルドログのロググループも併せて作成します。CodeDeployのプロバイダは、S3を指定するとアップロードから解凍まで実行されます。
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
SystemName:
Type: String
RepositoryName:
Type: String
BranchName:
Type: String
Resources:
CWLogGroup:
Type: 'AWS::Logs::LogGroup'
Properties:
LogGroupName: !Sub /aws/codebuild/${SystemName}-build-project
CodePipeline:
Type: 'AWS::CodePipeline::Pipeline'
DependsOn: CWLogGroup
Properties:
RoleArn: {'Fn::ImportValue': !Sub '${SystemName}-IAMCodePipelineRoleARN'}
ArtifactStore:
Type: 'S3'
Location: {'Fn::ImportValue': !Sub '${SystemName}-S3BucketArtifactStore'}
Stages:
- Name: 'Source'
Actions:
- Name: 'SourceAction'
ActionTypeId:
Category: 'Source'
Owner: 'AWS'
Version: 1
Provider: 'CodeStarSourceConnection'
Configuration:
ConnectionArn: {'Fn::ImportValue': !Sub '${SystemName}-ConnectionARN'}
FullRepositoryId: !Ref RepositoryName
BranchName: !Ref BranchName
OutputArtifactFormat: 'CODE_ZIP'
RunOrder: 1
OutputArtifacts:
- Name: 'Source'
- Name: 'Build'
Actions:
- Name: 'Build'
ActionTypeId:
Category: 'Build'
Owner: 'AWS'
Version: '1'
Provider: 'CodeBuild'
Configuration:
ProjectName: !Sub '${SystemName}-build-Project'
RunOrder: 1
InputArtifacts:
- Name: 'Source'
OutputArtifacts:
- Name: 'Build'
- Name: 'Deploy'
Actions:
- Name: 'Deploy'
ActionTypeId:
Category: 'Deploy'
Owner: 'AWS'
Version: '1'
Provider: 'S3'
Configuration:
BucketName: {'Fn::ImportValue': !Sub '${SystemName}-S3bucketForOrigin'}
Extract: 'true'
RunOrder: 1
InputArtifacts:
- Name: 'Build'
CodeBuildProject:
Type: 'AWS::CodeBuild::Project'
DependsOn: CWLogGroup
Properties:
Name: !Sub '${SystemName}-build-Project'
Source:
InsecureSsl: false
Type: 'CODEPIPELINE'
Artifacts:
EncryptionDisabled: false
Name: !Sub '${SystemName}-build-Project'
Packaging: 'NONE'
Type: 'CODEPIPELINE'
Cache:
Type: 'NO_CACHE'
Environment:
ComputeType: 'BUILD_GENERAL1_SMALL'
Image: 'aws/codebuild/amazonlinux2-x86_64-standard:3.0'
ImagePullCredentialsType: 'CODEBUILD'
PrivilegedMode: true
Type: 'LINUX_CONTAINER'
ServiceRole: {'Fn::ImportValue': !Sub '${SystemName}-IAMCodeBuildRoleARN'}
TimeoutInMinutes: 60
QueuedTimeoutInMinutes: 480
EncryptionKey: !Sub 'arn:aws:kms:${AWS::Region}:${AWS::AccountId}:alias/aws/s3'
BadgeEnabled: false
LogsConfig:
CloudWatchLogs:
Status: 'ENABLED'
S3Logs:
Status: 'DISABLED'
EncryptionDisabled: false
NestedStackテンプレートの更新(親スタック)
作成済みの親スタックのテンプレートにCodePipeline関連のテンプレートとParameters(RepositoryName
、BranchName
)を追加します。また、Parameterが増えてきたのでParameterGroupで整理しました。PIPELINEリソースは、DependsOn属性で依存関係を設定します。
AWSTemplateFormatVersion: '2010-09-09'
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
-
Label:
default: System Name Configuration
Parameters:
- SystemName
-
Label:
default: Hosting Configuration
Parameters:
- BucketName
- HostedZone
- MinimumProtocolVersion
- SslSupportMethod
- CertificateARN
-
Label:
default: Pipeline Configuration
Parameters:
- RepositoryName
- BranchName
Parameters:
SystemName:
Type: String
Default: example
BucketName:
Type: String
Default: www.example.xxx
HostedZone:
Type: String
Default: ZXXXXXXXXXXXXXXXXXXXX
MinimumProtocolVersion:
Type: String
Default: TLSv1.2_2019
AllowedValues:
- SSLv3
- TLSv1
- TLSv1.1_2016
- TLSv1.2_2018
- TLSv1.2_2019
- TLSv1_2016
SslSupportMethod:
Type: String
Default: sni-only
AllowedValues:
- sni-only
- static-ip
- vip
CertificateARN:
Type: String
Default: ''
RepositoryName:
Type: String
BranchName:
Type: String
Resources:
HOSTING:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: 'https://cfn-template-XXXXXXXXXXXX.s3-ap-northeast-1.amazonaws.com/hosting/hosting.yml'
Parameters:
SystemName: !Sub ${SystemName}
BucketName: !Sub ${BucketName}
HostedZone: !Sub ${HostedZone}
MinimumProtocolVersion: !Sub ${MinimumProtocolVersion}
SslSupportMethod: !Sub ${SslSupportMethod}
CertificateARN: !Sub ${CertificateARN}
DNS:
Type: AWS::CloudFormation::Stack
DependsOn: HOSTING
Properties:
TemplateURL: 'https://cfn-template-XXXXXXXXXXXX.s3-ap-northeast-1.amazonaws.com/hosting/dnsrecord.yml'
Parameters:
SystemName: !Sub ${SystemName}
BucketName: !Sub ${BucketName}
HostedZone: !Sub ${HostedZone}
ARTIFACT:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: 'https://cfn-template-XXXXXXXXXXXX.s3-ap-northeast-1.amazonaws.com/hosting/pipeline-artifact.yml'
Parameters:
SystemName: !Sub ${SystemName}
IAMPIPELINE:
Type: AWS::CloudFormation::Stack
Properties:
TemplateURL: 'https://cfn-template-XXXXXXXXXXXX.s3-ap-northeast-1.amazonaws.com/hosting/pipeline-iam.yml'
Parameters:
SystemName: !Sub ${SystemName}
PIPELINE:
Type: AWS::CloudFormation::Stack
DependsOn:
- ARTIFACT
- IAMPIPELINE
- HOSTING
Properties:
TemplateURL: 'https://cfn-template-XXXXXXXXXXXX.s3-ap-northeast-1.amazonaws.com/hosting/pipeline.yml'
Parameters:
SystemName: !Sub ${SystemName}
RepositoryName: !Sub ${RepositoryName}
BranchName: !Sub ${BranchName}
NestedStackの実行(更新)
子スタックのテンプレートをS3バケットに格納し、親スタックを更新します。
パラメータでGitHubのRipositoryName(GitHubアカウント名/リポジトリ名)、Branchを入力します。
親スタックを更新すると追加した子スタックが作成されてすべてのステータスがCREATE_COMPLETEになったら完了です。
CodePipelineを見るとPipelineが実行されてDeployが完了していればOKです。
動作確認
URLを確認しましょう。実施した環境ではAngulerのサンプルソースコードをGitHubに格納していたので以下のページが表示されました。
各テンプレートをS3バケットに格納してご自身の環境に応じてParametersを変えて頂ければそのまま実行できるかと思います。
最後に
初回のCFnテンプレートの作成は、GUIの操作よりリソースの作成に時間がかかるのがデメリットですが、同様の構成を横展開しやすくなるのとパラメータや構成がコードで確認できるようになるのがメリットだと思います。様々なタイプのテンプレートをどんどん作っていきたいと思います。